package io.mft.runtime;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import io.mft.api.domain.Api;
import io.mft.api.domain.ApiFunctionType;
import io.mft.commons.config.ConfigProvider;
import io.mft.commons.config.datasource.DSConfig;
import io.mft.commons.config.datasource.DSConfigSource;
import io.mft.commons.config.mq.MqConfig;
import io.mft.commons.config.mq.MqConfig.ConfigValue;
import io.mft.commons.exception.MFTException;
import io.mft.commons.function.ExecuteReturn;
import io.mft.commons.multithreading.worker.Work;
import io.mft.commons.multithreading.worker.WorkManager;
import io.mft.dev.base.ApiThreadExecutorConfig;
import io.mft.dev.base.MFTRuntimeConfig;
import io.mft.dev.base.cache.RegionConfig;
import io.mft.dev.base.init.MFTFrameworkInitor;
import io.mft.flow.core.FlowExecutor;
import io.mft.runtime.cache.ApiCache;
import io.mft.runtime.config.ConfigLoaderProvider;
import io.mft.runtime.config.RuntimeResource;
import io.mft.runtime.function.ApiContext;
import io.mft.runtime.function.ApiExecutorObject;
import io.mft.runtime.function.ApiFunctionRequest;
import io.mft.runtime.function.ApiFunctionResponse;
import io.mft.runtime.function.combination.ApiFunctionExecutorFlowRegister;
import io.mft.runtime.function.combination.ApiFunctionProcessExecutorFlowRegister;
import io.mft.runtime.function.initor.ApiFunctionExecutorInitor;
import io.mft.runtime.function.initor.CombinationApiFunctionExecutorInitor;
import io.mft.runtime.thread.AsyncExecuteThreadPool;

/**
 * 接口运行时
 * 
 * @说明：TODO
 * @类型名称：MFTApiRunTime
 * @创建者: 胡常建
 * @创建时间: 2021年1月26日 下午3:24:29
 * @修改者: 胡常建
 * @修改时间: 2021年1月26日 下午3:24:29
 */
public class MFTApiExecutor{
	private static Logger logger = LoggerFactory.getLogger(MFTApiExecutor.class);
	private static MFTApiExecutor instance = new MFTApiExecutor();
	public static MFTApiExecutor getInstance() {
		return instance;
	}
	String appId ;
	private RuntimeResource runtimeResource;
	private WorkManager apiFunctionInitorWorkManager;
	private ApiFunctionProcessExecutorFlowRegister apiFunctionProcessExecutorFlowRegister;
	private ApiFunctionExecutorFlowRegister apiExecutorFlowRegister;
	private FlowExecutor flowExecutor=new FlowExecutor();
	private ConfigLoaderProvider configLoaderProvider;
	private ApiFunctionExecutorInitor apiFunctionExecutorInitor;
	private CombinationApiFunctionExecutorInitor combinationApiFunctionExecutorInitor;
	private MFTApiExecutor() {
		try {
			ApiCache apiCache=new ApiCache();
			RuntimeResource runtimeResource=new RuntimeResource(apiCache, flowExecutor);
			this.runtimeResource=runtimeResource;
			configLoaderProvider=new ConfigLoaderProvider();
			appId=ConfigProvider.get("app.key","");
			apiFunctionInitorWorkManager=WorkManager.getManager(MFTApiExecutor.class.getName()+".apiFunctionInitor", 1, 1);
			apiFunctionProcessExecutorFlowRegister=new ApiFunctionProcessExecutorFlowRegister(flowExecutor);
			apiExecutorFlowRegister=new ApiFunctionExecutorFlowRegister(flowExecutor);
			apiFunctionExecutorInitor = new ApiFunctionExecutorInitor(apiCache);
			combinationApiFunctionExecutorInitor = new CombinationApiFunctionExecutorInitor(apiCache);
		} catch (Throwable e) {
			logger.error("MFT运行时初始化异常",e);
		}

	}

	public MFTRuntimeConfig getRuntimeConfig() {
		return runtimeResource.runtimeConfig();
	}
	
	public ConfigLoaderProvider getConfigLoaderProvider() {
		return configLoaderProvider;
	}
	public void registerApi(Api api,boolean isReload) {
		runtimeResource.getApiCache().addApi(api);
		if (api.getApiFunctionType()==ApiFunctionType.processExecutor) {
			apiFunctionProcessExecutorFlowRegister.register(api,isReload);
			apiFunctionExecutorInitor.init(api);
		}else if (api.getApiFunctionType()==ApiFunctionType.combination) {
			apiExecutorFlowRegister.register(api,isReload);
			combinationApiFunctionExecutorInitor.init(api);
		}
	}
	public void unregisterApi(String apiKey) {
		
	}
	
	public void registerThreadExecutorConfig(ApiThreadExecutorConfig config) {
		AsyncExecuteThreadPool.registerThreadExecutor(config);
	}
	
	public void registerLog4jConfig(Properties config) {
		if (!config.isEmpty()) {
			//PropertyConfigurator.configure(config);
		}
	}
	public void registerDSConfigSource(DSConfigSource dsConfigSource) {
	}
	
	public void registerRegionConfig(RegionConfig<?> regionConfig) {
	}
	public void registerMQConfigValue(ConfigValue mqcConfigValue) {
	}
	public String getAppId() {
		return appId;
	}

	public RuntimeResource getRuntimeResource() {
		return runtimeResource;
	}
	public void start() {
		AsyncExecuteThreadPool.initAsyncThreadPool();
		List<Api> apis = configLoaderProvider.apiConfigLoader().load(appId);
		DSConfig dsConfig= configLoaderProvider.datasourceConfigLoader().load(appId);
		List<MqConfig> mqConfigs=configLoaderProvider.mqConfigLoader().load(appId);
		List<RegionConfig<?>> regionConfigs=configLoaderProvider.cacheRegionConfigLoader().load(appId);
		List<ApiThreadExecutorConfig> apiThreadExecutorConfigs=configLoaderProvider.apiThreadExecutorConfigLoader().load(appId);
		runtimeResource.setDsConfig(dsConfig);
		runtimeResource.setMqConfigs(mqConfigs);
		runtimeResource.setRegionConfigs(regionConfigs);
		runtimeResource.setApiThreadExecutorConfigs(apiThreadExecutorConfigs);
		List<Api> processApis=new ArrayList<>();
		List<Api> combinationApis=new ArrayList<>();
		apis.forEach(api->{
			if (api.getApiFunctionType()==ApiFunctionType.processExecutor) {
				processApis.add(api);
			}
			if (api.getApiFunctionType()==ApiFunctionType.combination) {
				combinationApis.add(api);
			}
			
		});
		processApis.forEach(api->{
			registerApi(api, false);
		});
		combinationApis.forEach(api->{
			registerApi(api, false);
		});
		//apiFunctionInitorWorkManager.acceptFutureWork(apiFunctionInitor);
		runtimeResource.setInited(true);
	}
	

	
	Work apiFunctionInitor=new Work() {

		@Override
		public Object handle() {
			Thread.currentThread().setContextClassLoader(MFTApiExecutor.class.getClassLoader());
			while (true) {
				try {
					TimeUnit.SECONDS.sleep(10);
					MFTFrameworkInitor.reloadConfigToConfigProvider();
					notifyChangeApi();
					notifyChangeDsConfig();
					notifyChangeMQConfig();
					notifyChangeCacheRegionConfig();
				} catch (Exception e) {
					logger.error("",e);
				}
			}
		}
	};
	
	private void notifyChangeApi() {
		try {
			List<Api> apis = configLoaderProvider.apiConfigLoader().load(appId);
			for (Api api : apis) {
				try {
					Api t=runtimeResource.getApiCache().getApi(api.getKey());
					if (t==null) {
						registerApi(api,false);
						logger.warn("新增API["+api.getKey()+"]["+api.getName()+"]配置，注册至API运行容器");
					}else {
						String id1=t.uniqueId();
						String id2=api.uniqueId();
						if (!id1.equals(id2)) {
							registerApi(api,true);
							logger.warn("API["+api.getKey()+"]["+api.getName()+"]配置更新，注册至API运行容器");
						}
					}
				} catch (Exception e) {
					logger.error("API["+api.getKey()+"]检查异常",e);
				}
			}
		} catch (Exception e) {
			logger.error("",e);
		}
	}
	
	private void notifyChangeDsConfig() {
		try {
			DSConfig dsConfig= configLoaderProvider.datasourceConfigLoader().load(appId);
			if (ObjectUtil.isNotNull(dsConfig)) {
				boolean dsChange=this.runtimeResource.getDsConfig().uniqueId().equals(dsConfig.uniqueId())?false:true;
				if (dsChange) {
					Set<Entry<String, DSConfigSource>> configEntries=dsConfig.entrySet();
					for (Entry<String, DSConfigSource> entry : configEntries) {
						registerDSConfigSource(entry.getValue());
					}
					this.runtimeResource.setDsConfig(dsConfig);
				}
			}

		} catch (Exception e) {
			logger.error("",e);
		}
	}
	private void notifyChangeMQConfig() {
		this.runtimeResource.getMqConfigs().forEach(mqConfig->{
			mqConfig.values().forEach(configValue->{
				//registerMQConfigValue(configValue);
			});
		});
	}
	
	private void notifyChangeCacheRegionConfig() {
		this.runtimeResource.getRegionConfigs().forEach(regionConfig->{
			registerRegionConfig(regionConfig);
		});
	}
	/**
	 * 执行API接口请求
	 * 
	 * @说明: TODO
	 * @方法名称: execute
	 * @参数 @param apiKey
	 * @参数 @param parameter
	 * @参数 @return
	 * @返回类型 ApiFunctionResponse
	 * @创建者: 胡常建
	 * @创建时间: 2021年1月26日 下午4:26:30
	 * @修改者: 胡常建
	 * @修改时间: 2021年1月26日 下午4:26:30
	 */
	public ApiFunctionResponse execute(String apiKey, ApiFunctionRequest request) {
		ApiFunctionResponse response = new ApiFunctionResponse();
		execute(apiKey, request, response);
		return response;
	}
	
	public ExecuteReturn execute(String apiKey, ApiFunctionRequest request,ApiFunctionResponse response) {
		try {
			if (!runtimeResource.isInited()) {
				throw new MFTException(-1, "系统运行时资源未初始化完成,请等待");
			}
			ApiExecutorObject apiExecutorObject = runtimeResource.getApiCache().getApiExecutorObject(apiKey);
			if (ObjectUtil.isNull(apiExecutorObject)) {
				throw new MFTException("接口[" + apiKey + "]不存在,请检查！");
			}
			String APIRequestID = UUID.fastUUID().toString(true);
			request.setRequestId(APIRequestID);
			ApiContext context = new ApiContext(apiExecutorObject, request, response);
			
			ExecuteReturn executeReturn = apiExecutorObject.getFunctionExecutor().doExecute(context);
			if (executeReturn.getCode()!=0) {
				response.setErrorInfo(executeReturn.getMessage());
				response.setErrorNo(executeReturn.getCode());
			}
			return executeReturn;
		} catch (MFTException e) {
			throw e;
		} catch (Exception e) {
			throw new MFTException("", e);
		}
	}
}
